﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Threading;
using System.Xml.Linq;
using Microsoft.Phone.BackgroundAudio;
using WindowsPhoneAudioStreaming.Audio.Streaming;
using WindowsPhoneAudioStreaming.CurrentlyPlayingService;
using WindowsPhoneAudioStreaming.CurrentlyPlayingService.Data;
using WindowsPhoneAudioStreaming.SampleApplication.Helpers;

namespace WindowsPhoneAudioStreaming.SampleApplication
{
    public class MainViewModel : INotifyPropertyChanged
    {
        #region Variables
        
        DispatcherTimer _UIRefreshTimer;
        DispatcherTimer _LatestSongListTimer;
        INowPlayingService _nowPlayingService;
        private const string tileFileName = "/Shared/ShellContent/backgroundart.jpg";
        private PlaybackMode _CurrentPlaybackMode = PlaybackMode.Streaming;
        #endregion

        #region Properties

        public Repository Repository { get; set; }

        #region Collections
        /// <summary>
        /// A collection for ItemViewModel objects.
        /// </summary>
        public ObservableCollection<TrackModel> RecentTracks
        {
            get
            {
                if (_nowPlayingService != null && _nowPlayingService.RecentTracks != null)
                {
                    return _nowPlayingService.RecentTracks;
                }

                return new ObservableCollection<TrackModel>();
            }
        }
        public ObservableCollection<TrackViewModel> AudioStreams { get; private set; }
        public ObservableCollection<PodcastViewModel> Podcasts { get; private set; }
        public ObservableCollection<ProgramViewModel> Programs { get; private set; }
        #endregion

        #region Current Items
        public string Today
        {
            get { return DateTime.Now.DayOfWeek.ToString(); }
        }
        public TrackModel CurrentTrack
        {
            get
            {
                return _nowPlayingService.CurrentTrack;
            }
        }

        private PodcastViewModel _CurrentPodcastTrack;
        public PodcastViewModel CurrentPodcastTrack
        {
            get
            {
                return _CurrentPodcastTrack;
            }
            set
            {
                _CurrentPodcastTrack = value;
                NotifyPropertyChanged("CurrentPodcastItem");
            }

        }

        /// <summary>
        /// Has the system got podcast tracks
        /// </summary>
        public bool HasPodcasts
        {
            get
            {
                return this.Podcasts != null && this.Podcasts.Count > 0;
            }
        }

        public ProgramViewModel CurrentProgram
        {
            get
            {
                return GetCurrentProgram();
            }
        }

        private ProgramViewModel _CurrentSelectedProgram;
        public ProgramViewModel CurrentSelectedProgram
        {
            get
            {
                return _CurrentSelectedProgram;
            }
            set
            {
                _CurrentSelectedProgram = value;
                NotifyPropertyChanged("CurrentSelectedProgram");
            }
        }

        public bool HasCurrentProgram
        {
            get
            {
                return !String.IsNullOrEmpty(this.CurrentProgram.ProgramName);
            }
        }

        private TrackViewModel _CurrentStream;
        public TrackViewModel CurrentStream
        {
            get
            {
                return _CurrentStream;
            }
            set
            {
                if (_CurrentPlaybackMode == PlaybackMode.Streaming
                    && _CurrentStream != null
                    && _CurrentStream.TrackName != value.TrackName)
                {
                    if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
                    {
                        //Need to reset the background audio player
                        BackgroundAudioPlayer.Instance.Stop();
                        BackgroundAudioPlayer.Instance.Close();
                    }
                }

                _CurrentStream = value;
                NotifyPropertyChanged("CurrentStream");
            }
        }
        #endregion

        #region Podcasts
        private ProgramViewModel _FeaturedProgram;
        public ProgramViewModel FeaturedProgram
        {
            get
            {
                return _FeaturedProgram;
            }
            set
            {
                _FeaturedProgram = value;
                NotifyPropertyChanged("FeaturedProgram");
            }
        }

        private bool _HasFeaturedProgram;
        public bool HasFeaturedProgram
        {
            get
            {
                return _HasFeaturedProgram;
            }
            set
            {
                _HasFeaturedProgram = value;
                NotifyPropertyChanged("HasFeaturedProgram");
            }
        }

        private ProgramViewModel _SubFeaturedProgram1;
        public ProgramViewModel SubFeaturedProgram1
        {
            get
            {
                return _SubFeaturedProgram1;
            }
            set
            {
                _SubFeaturedProgram1 = value;
                NotifyPropertyChanged("SubFeaturedProgram1");
            }
        }

        private bool _HasSubFeaturedProgram1;
        public bool HasSubFeaturedProgram1
        {
            get
            {
                return _HasSubFeaturedProgram1;
            }
            set
            {
                _HasSubFeaturedProgram1 = value;
                NotifyPropertyChanged("HasSubFeaturedProgram1");
            }
        }

        private ProgramViewModel _SubFeaturedProgram2;
        public ProgramViewModel SubFeaturedProgram2
        {
            get
            {
                return _SubFeaturedProgram2;
            }
            set
            {
                _SubFeaturedProgram2 = value;
                NotifyPropertyChanged("SubFeaturedProgram2");
            }
        }

        private bool _HasSubFeaturedProgram2;
        public bool HasSubFeaturedProgram2
        {
            get
            {
                return _HasSubFeaturedProgram2;
            }
            set
            {
                _HasSubFeaturedProgram2 = value;
                NotifyPropertyChanged("HasSubFeaturedProgram2");
            }
        }

        private ProgramViewModel _SubFeaturedProgram3;
        public ProgramViewModel SubFeaturedProgram3
        {
            get
            {
                return _SubFeaturedProgram3;
            }
            set
            {
                _SubFeaturedProgram3 = value;
                NotifyPropertyChanged("SubFeaturedProgram3");
            }
        }

        private bool _HasSubFeaturedProgram3;
        public bool HasSubFeaturedProgram3
        {
            get
            {
                return _HasSubFeaturedProgram3;
            }
            set
            {
                _HasSubFeaturedProgram3 = value;
                NotifyPropertyChanged("HasSubFeaturedProgram3");
            }
        }

        private ProgramViewModel _SubFeaturedProgram4;
        public ProgramViewModel SubFeaturedProgram4
        {
            get
            {
                return _SubFeaturedProgram4;
            }
            set
            {
                _SubFeaturedProgram4 = value;
                NotifyPropertyChanged("SubFeaturedProgram4");
            }
        }

        private bool _HasSubFeaturedProgram4;
        public bool HasSubFeaturedProgram4
        {
            get
            {
                return _HasSubFeaturedProgram4;
            }
            set
            {
                _HasSubFeaturedProgram4 = value;
                NotifyPropertyChanged("HasSubFeaturedProgram4");
            }
        }
        #endregion

        #region Station Details
        private string _StationName;
        /// <summary>
        /// What is the name of the radio station/application
        /// </summary>
        public string StationName
        {
            get
            {
                return _StationName;
            }
            set
            {
                _StationName = value;
                NotifyPropertyChanged("StationName");
            }
        }

        private string _Slogan;
        /// <summary>
        /// What is the slogan for the radio station/application
        /// </summary>
        public string Slogan
        {
            get
            {
                return _Slogan;
            }
            set
            {
                _Slogan = value;
                NotifyPropertyChanged("Slogan");
            }
        }
        #endregion

        #region Player Properties
        public bool IsPlayingStream
        {
            get
            {
                if (_CurrentPlaybackMode == PlaybackMode.Podcast)
                {
                    return false;
                }

                if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
                {
                    return true;
                }

                return false;
            }
        }

        public bool IsPlayingPodcasts
        {
            get
            {
                if (_CurrentPlaybackMode == PlaybackMode.Streaming)
                {
                    return false;
                }

                if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
                {
                    return true;
                }

                return false;
            }
        }

        private string _CurrentState;
        /// <summary>
        /// What is the position of the current track
        /// </summary>
        public string CurrentState
        {
            get
            {
                return _CurrentState;
            }
            set
            {
                _CurrentState = value;
                NotifyPropertyChanged("CurrentState");
            }
        }

        private string _CurrentPosition;
        public string CurrentPosition
        {
            get
            {
                return _CurrentPosition;
            }
            set
            {
                _CurrentPosition = value;
                NotifyPropertyChanged("CurrentPosition");
            }
        }

        private string _TimeRemaining;
        /// <summary>
        /// What is the time remaining on the current track
        /// </summary>
        public string TimeRemaining
        {
            get
            {
                return _TimeRemaining;
            }
            set
            {
                _TimeRemaining = value;
                NotifyPropertyChanged("TimeRemaining");
            }
        }

        private bool _IsPlayerIndeterminate;
        /// <summary>
        /// Is the player in an inderminate state
        /// </summary>
        public bool IsPlayerIndeterminate
        {
            get
            {
                return _IsPlayerIndeterminate;
            }
            set
            {
                _IsPlayerIndeterminate = value;
                NotifyPropertyChanged("IsPlayerIndeterminate");
            }
        }

        private double _MaximumPlayerDurationSeconds;
        /// <summary>
        /// what is the maximum number of seconds that can elapse in the player
        /// </summary>
        public double MaximumPlayerDurationSeconds
        {
            get
            {
                return _MaximumPlayerDurationSeconds;
            }
            set
            {
                _MaximumPlayerDurationSeconds = value;
                NotifyPropertyChanged("MaximumPlayerDurationSeconds");
            }
        }

        private double _PlayerCurrentSecondsElapsed;
        /// <summary>
        /// How many seconds have currently elapsed in the player
        /// </summary>
        public double PlayerCurrentSecondsElapsed
        {
            get
            {
                return _PlayerCurrentSecondsElapsed;
            }
            set
            {
                _PlayerCurrentSecondsElapsed = value;
                NotifyPropertyChanged("PlayerCurrentSecondsElapsed");
            }
        }

        private bool _IsPlayEnabled;
        /// <summary>
        /// Is 'play' enabled
        /// </summary>
        public bool IsPlayEnabled
        {
            get
            {
                return _IsPlayEnabled;
            }
            set
            {
                _IsPlayEnabled = value;
                NotifyPropertyChanged("IsPlayEnabled");
            }
        }

        private bool _IsPauseEnabled;
        /// <summary>
        /// Is 'pause' enabled
        /// </summary>
        public bool IsPauseEnabled
        {
            get
            {
                return _IsPauseEnabled;
            }
            set
            {
                _IsPauseEnabled = value;
                NotifyPropertyChanged("IsPauseEnabled");
            }
        }

        private bool _IsPreviousEnabled;
        /// <summary>
        /// Is the 'previous' track enabled
        /// </summary>
        public bool IsPreviousEnabled
        {
            get
            {
                return _IsPreviousEnabled;
            }
            set
            {
                _IsPreviousEnabled = value;
                NotifyPropertyChanged("IsPreviousEnabled");
            }
        }

        private bool _IsNextEnabled;
        /// <summary>
        /// Is the 'next' track enabled
        /// </summary>
        public bool IsNextEnabled
        {
            get
            {
                return _IsNextEnabled;
            }
            set
            {
                _IsNextEnabled = value;
                NotifyPropertyChanged("IsNextEnabled");
            }
        }
        #endregion

        /// <summary>
        /// Is there data loaded for the application
        /// </summary>
        public bool IsDataLoaded
        {
            get;
            private set;
        }

        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor for the application
        /// </summary>
        public MainViewModel()
        {
            _CurrentPlaybackMode = PlaybackMode.Podcast;

            // Initialize a timer to update the UI every half-second.
            _UIRefreshTimer = new DispatcherTimer();
            _UIRefreshTimer.Interval = TimeSpan.FromSeconds(0.5);
            _UIRefreshTimer.Tick += UpdateState;

            _LatestSongListTimer = new DispatcherTimer();
            _LatestSongListTimer.Interval = TimeSpan.FromSeconds(60);
            _LatestSongListTimer.Tick += (s, e) =>
            {
                RefreshPlayList();
            };


            this.AudioStreams = new ObservableCollection<TrackViewModel>();
            this.Programs = new ObservableCollection<ProgramViewModel>();
            this.Podcasts = new ObservableCollection<PodcastViewModel>();
            this.Podcasts.CollectionChanged += (s, e) =>
            {
                NotifyPropertyChanged("HasPodcasts");
                NotifyPropertyChanged("Podcasts");
                NotifyPropertyChanged("CurrentPodcastTrack");
            };

            this.Programs.CollectionChanged += (s, e) =>
                {
                    GetCurrentProgram();
                };
            
            BackgroundAudioPlayer.Instance.PlayStateChanged += Instance_PlayStateChanged;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Method fired when the page/view is loaded
        /// </summary>
        public void PageLoaded()
        {
            if (!this.IsDataLoaded)
            {
                LoadData();

                // Initialize a timer to update the UI every half-second.
                _UIRefreshTimer = new DispatcherTimer();
                _UIRefreshTimer.Interval = TimeSpan.FromSeconds(0.5);
                _UIRefreshTimer.Tick += UpdateState;

                if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
                {
                    // If audio was already playing when the app was launched, update the UI.
                    IsPlayerIndeterminate = false;
                    MaximumPlayerDurationSeconds = BackgroundAudioPlayer.Instance.Track.Duration.TotalSeconds;
                    UpdateButtons(true, false, true, true);
                    UpdateState(null, null);
                }
            }
            Deployment.Current.Dispatcher.BeginInvoke(delegate
            {
                RefreshPlayList();
                NotifyPropertyChanged("IsPlayingStream");
                NotifyPropertyChanged("IsPlayingPodcasts");
            });
        }

        /// <summary>
        /// Play/start the current audio stream
        /// </summary>
        public void PlayStreamingAudio()
        {
            LoadStreamingMode();

            BackgroundAudioPlayer.Instance.Play();
            RefreshPlayList();

        }

        /// <summary>
        /// Switch to listen to podcasts instead of audio streams
        /// </summary>
        public void PlayPodcasts()
        {
            LoadPodcastMode();

            BackgroundAudioPlayer.Instance.Play();

        }

        /// <summary>
        /// Pause the current audio stream
        /// </summary>
        public void Pause()
        {
            BackgroundAudioPlayer.Instance.Pause();
            RefreshPlayList();

        }

        /// <summary>
        /// Creates and adds a few ItemViewModel objects into the Items collection.
        /// </summary>
        public void LoadData()
        {
            var currentConfiguration = ConfigurationSetup.LoadConfiguration();
            LoadPrograms(currentConfiguration.ProgramUrl);

            this.Podcasts.Clear();
            this.AudioStreams = new ObservableCollection<TrackViewModel>(currentConfiguration.Streams.Select(c => new TrackViewModel() { 
                TrackName = c.Title,
                AlbumArt = c.ImageUri, 
                Album = c.StreamUri
            }).ToList());


            CurrentStream = this.AudioStreams.FirstOrDefault();

            #region Example of implementing now playing using 'Triple J'
            //_nowPlayingService = new PlayoutPlayingService();
            #endregion

            #region Example Of implementing now playing using 'Absolute radio'
            _nowPlayingService = new AbsoluteRadioPlayingService(Repository);
            _nowPlayingService.NumberRecentTracksToDisplay = 4;
            _nowPlayingService.CurrentDataService = new System.Uri(currentConfiguration.NowPlayingUrl);
            #endregion
            
            var nowPlayingList = currentConfiguration.Streams.ToPlaylist();
            _nowPlayingService.Playlist = new ObservableCollection<PlaylistTrack>(nowPlayingList);

            _nowPlayingService.TracksUpdated += (s, e) =>
            {
                NotifyPropertyChanged("RecentTracks");
                NotifyPropertyChanged("CurrentTrack");
                _nowPlayingService.UpdateLiveTile();
            };

            this.IsDataLoaded = true;
        }

        /// <summary>
        /// Refresh the playlist to show the most recent tracks
        /// </summary>
        public void RefreshPlayList()
        {
            _nowPlayingService.RefreshCurrentTrack();
        }
        
        /// <summary>
        /// Clears the playlist and plays the current track
        /// </summary>
        public void ClearPlaylistAndPlayCurrentTrack()
        {
            this.Podcasts.Add(CurrentPodcastTrack);
            LoadPodcastMode();
            PlayPodcasts();
        }

        #endregion

        #region Private Method

        /// <summary>
        /// Load the background audio to play in podcast mode
        /// </summary>
        private void LoadPodcastMode()
        {
            if (_CurrentPlaybackMode == PlaybackMode.Streaming)
            {
                BackgroundAudioPlayer.Instance.Stop();
                BackgroundAudioPlayer.Instance.Close();
                _CurrentPlaybackMode = PlaybackMode.Podcast;
            }

            var nowPlayingList = this.Podcasts.ToList().ToPlaylist();
            _nowPlayingService.Playlist = new ObservableCollection<PlaylistTrack>(nowPlayingList);
        }

        /// <summary>
        /// Load the player into streaming mode
        /// </summary>
        private void LoadStreamingMode()
        {
            if (_CurrentPlaybackMode == PlaybackMode.Podcast)
            {
                BackgroundAudioPlayer.Instance.Stop();
                BackgroundAudioPlayer.Instance.Close();
                _CurrentPlaybackMode = PlaybackMode.Streaming;
                _nowPlayingService.GetCurrentPlaylist();
            }


        }

        /// <summary>
        /// Load all the programs for the station
        /// </summary>
        /// <param name="xmlUrl"></param>
        private void LoadPrograms(string xmlUrl)
        {
            WebClient client = new WebClient();
            client.OpenReadCompleted += (sender, e) =>
            {
                if (e.Error != null)
                    return;

                Stream str = e.Result;
                XDocument programsDataXml = XDocument.Load(str);
                var programsData = from query in programsDataXml.Descendants("Program")
                                   select new ProgramViewModel
                                       {
                                           ProgramName = (string)query.Element("Name"),
                                           Description = (string)query.Element("Description"),
                                           Presenters = (string)query.Element("Presenters"),
                                           StartTime = (string)query.Element("StartTime"),
                                           EndTime = (string)query.Element("EndTime"),
                                           AssociatedPodcastUrl = (string)query.Element("AssociatedPodcastUrl"),
                                           ProgramImage = (string)query.Element("AssociatedImageUrl"),
                                           ProgramSlogan = (string)query.Element("ProgramSlogan"),
                                           Keywords = (string)query.Element("Keywords"),
                                           Schedule = (string)query.Element("StartTime") + " - " + (string)query.Element("EndTime")
                                           //PublishedDate = DateTime.Parse((string)query.Element("pubDate"))
                                       };

                str.Close();
                //Add the podcast to the list
                if (programsData != null && programsData.Count() > 0)
                {
                    this.Programs = new ObservableCollection<ProgramViewModel>(programsData.ToList());
                    NotifyPropertyChanged("Programs");
                    NotifyPropertyChanged("CurrentProgram");
                    NotifyPropertyChanged("HasCurrentProgram");


                    if (this.Programs.Count > 0)
                    {
                        this.FeaturedProgram = this.Programs[0];
                        this.HasFeaturedProgram = true;
                    }

                    this.HasSubFeaturedProgram1 = this.Programs.Count > 1;
                    this.HasSubFeaturedProgram2 = this.Programs.Count > 2;
                    this.HasSubFeaturedProgram3 = this.Programs.Count > 3;
                    this.HasSubFeaturedProgram3 = this.Programs.Count > 4;

                    if (this.HasSubFeaturedProgram1)
                    {
                        this.SubFeaturedProgram1 = this.Programs[1];
                    }

                    if (this.HasSubFeaturedProgram2)
                    {
                        this.SubFeaturedProgram2 = this.Programs[2];
                    }

                    if (this.HasSubFeaturedProgram3)
                    {
                        this.SubFeaturedProgram3 = this.Programs[3];
                    }

                    if (this.HasSubFeaturedProgram4)
                    {
                        this.SubFeaturedProgram4 = this.Programs[4];
                    }
                }
            };
            client.OpenReadAsync(new Uri(xmlUrl, UriKind.Absolute));
        }
        
        /// <summary>
        /// Get the current program
        /// </summary>
        /// <returns></returns>
        private ProgramViewModel GetCurrentProgram()
        {
            TimeSpan timeNow = DateTime.Now.TimeOfDay;
            var currentProgram = Programs.FirstOrDefault(p => p.StartTimeSpan.CompareTo(timeNow) <= 0
                && p.EndTimeSpan.CompareTo(timeNow) >= 0);

            if (currentProgram != null)
            {
                return currentProgram;
            }
            else
            {
                return new ProgramViewModel()
                {
                    ProgramName = "No specified program information",
                    ProgramSlogan = "could be found",
                    Description = "Please check the programs page for available programs"
                };
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// Helper method to update the state of the ApplicationBar.Buttons
        /// </summary>
        /// <param name="prevBtnEnabled"></param>
        /// <param name="playBtnEnabled"></param>
        /// <param name="pauseBtnEnabled"></param>
        /// <param name="nextBtnEnabled"></param>
        void UpdateButtons(bool prevBtnEnabled, bool playBtnEnabled, bool pauseBtnEnabled, bool nextBtnEnabled)
        {
            // Set the IsEnabled state of the ApplicationBar.Buttons array
            this.IsPreviousEnabled = prevBtnEnabled;
            this.IsPlayEnabled = playBtnEnabled;
            this.IsPauseEnabled = pauseBtnEnabled;
            this.IsNextEnabled = nextBtnEnabled;
        }

        /// <summary>
        /// PlayStateChanged event handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Instance_PlayStateChanged(object sender, EventArgs e)
        {
            switch (BackgroundAudioPlayer.Instance.PlayerState)
            {
                case PlayState.Playing:
                    // Update the UI.
                    IsPlayerIndeterminate = false;
                    MaximumPlayerDurationSeconds = BackgroundAudioPlayer.Instance.Track.Duration.TotalSeconds;
                    UpdateButtons(true, false, true, true);
                    UpdateState(null, null);

                    // Start the timer for updating the UI.
                    _UIRefreshTimer.Start();
                    NotifyPropertyChanged("IsPlayingStream");
                    NotifyPropertyChanged("IsPlayingPodcasts");
                    break;

                case PlayState.Paused:
                    // Update the UI.
                    UpdateButtons(true, true, false, true);
                    UpdateState(null, null);

                    // Stop the timer for updating the UI.
                    _UIRefreshTimer.Stop();
                    break;
            }
        }

        /// <summary>
        /// Updates the status indicators including the State, Track title, 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UpdateState(object sender, EventArgs e)
        {
            CurrentState = string.Format("State: {0}", BackgroundAudioPlayer.Instance.PlayerState);

            if (BackgroundAudioPlayer.Instance.Track != null)
            {
                CurrentStream = new TrackViewModel(BackgroundAudioPlayer.Instance.Track);

                // Set the current position on the ProgressBar.
                if (BackgroundAudioPlayer.Instance != null && BackgroundAudioPlayer.Instance.Position != null
                    && BackgroundAudioPlayer.Instance.Track != null)
                {
                    PlayerCurrentSecondsElapsed = BackgroundAudioPlayer.Instance.Position.TotalSeconds;

                    // Update the current playback position.
                    TimeSpan position = new TimeSpan();
                    position = BackgroundAudioPlayer.Instance.Position;
                    CurrentPosition = String.Format("{0:d2}:{1:d2}:{2:d2}", position.Hours, position.Minutes, position.Seconds);

                    if (BackgroundAudioPlayer.Instance.Track.Duration != null)
                    {
                        // Update the time remaining digits.
                        TimeSpan timeRemaining = new TimeSpan();
                        timeRemaining = BackgroundAudioPlayer.Instance.Track.Duration - position;
                        TimeRemaining = String.Format("-{0:d2}:{1:d2}:{2:d2}", timeRemaining.Hours, timeRemaining.Minutes, timeRemaining.Seconds);
                    }
                }
                else
                {
                    PlayerCurrentSecondsElapsed = 0;
                    CurrentPosition = "";
                    TimeRemaining = "";
                }

            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            if (null != PropertyChanged)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}